// --------------------------------------------------------------------------------------------------------------------
// <copyright file="VirtualMarket.Matching.FillOrKill.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge.Plugins
{
    #region Using Directives

    using System;

    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Events;
    using Org.OpenTrader.Framework.Forge.Interfaces;

    #endregion

    /// <summary>
    /// The virtual market.
    /// </summary>
    public partial class VirtualMarket
    {
        #region Methods

        /// <summary>
        /// Handle fill or kill orders
        /// Those order are not kept in a list like limit order
        /// Indeed, whatever happen on quantity/price available to grab, the order is cancel, then done
        /// </summary>
        /// <param name="order">
        /// The order.
        /// </param>
        /// <param name="marketBook">
        /// The marketBook.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        private void MatchFillOrKillSynchronously(IOrder order, IMarketBook marketBook, RemotableEventHandler<OrderEventArgs> handler)
        {
            // Order is now ACK
            this.OrderPlugin.RaiseEventAckOrder(order.ID, order.Way, order.Quantity, order.Quantity, order.Price, handler);

            var availableQuantity = 0;
            var bestPrice = double.NaN;

            switch (order.Way)
            {
                case EOrderWay.Buy:
                    if (marketBook == null)
                    {
                        // invalid market book
                        goto done;
                    }

                    if (marketBook.Asks == null)
                    {
                        // invalid asks
                        goto done;
                    }

                    if (!marketBook.Asks[0].HasValue)
                    {
                        // invalid best ask
                        goto done;
                    }

                    if (order.Price < marketBook.Asks[0].Price)
                    {
                        // we wan't to buy cheaper than available
                        goto done;
                    }

                    // We execute at order price or better
                    bestPrice = marketBook.Asks[0].Price;
                    availableQuantity = marketBook.Asks[0].Size;
                    break;

                case EOrderWay.Sell:
                    if (marketBook == null)
                    {
                        // invalid marketbook
                        goto done;
                    }

                    if (marketBook.Bids == null)
                    {
                        // invalid bids
                        goto done;
                    }

                    if (!marketBook.Bids[0].HasValue)
                    {
                        // invalid best bid
                        goto done;
                    }

                    if (order.Price > marketBook.Bids[0].Price)
                    {
                        // we wan't to sell more expensive than best bidder gives for it
                        goto done;
                    }

                    // We execute at order price or better
                    bestPrice = marketBook.Bids[0].Price;
                    availableQuantity = marketBook.Bids[0].Size;
                    break;

                default:
                    return;
            }

            done:
            var cancelQuantity = Math.Max(0, order.Quantity - availableQuantity);
            var doneQuantity = order.Quantity - cancelQuantity;

            // We executed a part
            if (doneQuantity > 0)
            {
                this.OrderPlugin.RaiseEventOrderExecution(order.ID, order.Way, bestPrice, doneQuantity, cancelQuantity, handler);
            }

            // A part is cancelled (may be all the size if nothing was catchable)
            if (cancelQuantity > 0)
            {
                this.OrderPlugin.RaiseEventAckCancel(order.ID, order.Way, cancelQuantity, handler);
            }

            // Order is now done
            this.OrderPlugin.RaiseEventOutReport(order.ID, order.Way, handler);
        }

        #endregion
    }
}